package com.atguigu.tingshu.search.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldSort;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Operator;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Highlight;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.CategoryFeignClient;
import com.atguigu.tingshu.common.cache.GuiguCache;
import com.atguigu.tingshu.common.config.pool.ServiceThreadPoolConfig;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AssertUtil;
import com.atguigu.tingshu.common.util.PinYinUtils;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategory3;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.search.utlis.AlbumCalculation;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import io.micrometer.common.util.StringUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"unchecked"})
public class SearchServiceImpl implements SearchService {
    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;
    @Autowired
    private UserInfoFeignClient userInfoFeignClient;
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;
    @Autowired
    private CategoryFeignClient categoryFeignClient;
    @Autowired
    private ElasticsearchClient elasticsearchClient;
    @Autowired
    private RedisTemplate redisTemplate;
    // 主播专辑上架的时候更新布隆过滤器 这样前端才可以到数据库查询数据避免缓存穿透
    @Autowired
    private RedissonClient redissonClient;

    @Override
    public void upperAlbumInfoToEs(Long albumId) {
        // 上架专辑
        log.info("上架专辑：{}", albumId);
        if (albumId == null) {
            throw new GuiguException(ResultCodeEnum.ALBUM_NOT_EXIST_ERROR);
        }
        // 创建 AlbumInfoIndex 封装数据
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();
        // 异步编排优化
        //   // 1. 远程调用获取专辑信息
        CompletableFuture<AlbumListVo> albumListVoFuture = CompletableFuture.supplyAsync(() -> {
            Result<AlbumListVo> albumInfoByIdResult = albumInfoFeignClient.getAlbumInfoById(albumId);
            if (albumInfoByIdResult == null
                    || !albumInfoByIdResult.getCode().equals(ResultCodeEnum.SUCCESS.getCode())
                    || albumInfoByIdResult.getData() == null) {
                log.error("远程调用获取AlbumInfo失败");
                throw new GuiguException(ResultCodeEnum.ALBUM_NOT_EXIST_ERROR);
            }
            AlbumListVo albumListVo = albumInfoByIdResult.getData();
            albumInfoIndex.setId(albumId);
            BeanUtils.copyProperties(albumListVo, albumInfoIndex);
            // 计算热度值
            AlbumCalculation.AlbumHotScoreCalculation(albumInfoIndex);
            return albumListVo;
        }, ServiceThreadPoolConfig.getExecutor());

        CompletableFuture<Void> attributeValueCompletableFuture = CompletableFuture.runAsync(() -> {
            // 2. 远程调用获取专辑属性信息
            Result<List<AttributeValueIndex>> listAttributeValueResult = albumInfoFeignClient.findAttributesByAlbumId(albumId);
            if (listAttributeValueResult == null
                    || !listAttributeValueResult.getCode().equals(ResultCodeEnum.SUCCESS.getCode())
                    || listAttributeValueResult.getData() == null) {
                log.error("远程调用获取AlbumInfo失败");
                throw new GuiguException(ResultCodeEnum.ALBUM_NOT_EXIST_ERROR);
            }
            albumInfoIndex.setAttributeValueIndexList(listAttributeValueResult.getData());

        }, ServiceThreadPoolConfig.getExecutor());
        // 3. 获取userId远程调用C微服务 获取用户信息  在1的任务之后执行
        CompletableFuture<Void> userInfoCompletableFuture = albumListVoFuture.thenAcceptAsync((albumListVo) -> {
            Result<UserInfoVo> userInfoByIdResult = userInfoFeignClient.getUserInfoById(albumListVo.getUserId());
            if (userInfoByIdResult == null
                    || !userInfoByIdResult.getCode().equals(ResultCodeEnum.SUCCESS.getCode())
                    || userInfoByIdResult.getData() == null) {
                log.error("远程调用获取UserInfo失败");
                throw new GuiguException(ResultCodeEnum.USER_NOT_EXIST_ERROR);
            }
            UserInfoVo userInfoVo = userInfoByIdResult.getData();
            albumInfoIndex.setAnnouncerName(userInfoVo.getNickname());
        }, ServiceThreadPoolConfig.getExecutor());

        // 4. 获取专辑三级分类id远程调用category微服务 获取分类视图对象 在1的任务之后执行
        CompletableFuture<Void> categoryCompletableFuture = albumListVoFuture.thenAcceptAsync((albumListVo) -> {
            Result<BaseCategoryView> baseCategoryViewResult = categoryFeignClient.getBaseCategoryViewByCategory3Id(albumListVo.getCategory3Id());
            if (baseCategoryViewResult == null
                    || !baseCategoryViewResult.getCode().equals(ResultCodeEnum.SUCCESS.getCode())
                    || baseCategoryViewResult.getData() == null) {
                log.error("远程调用获取BaseCategoryView失败");
                throw new GuiguException(ResultCodeEnum.ALBUM_NOT_EXIST_ERROR);
            }
            BaseCategoryView baseCategoryView = baseCategoryViewResult.getData();
            albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
            albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());
        }, ServiceThreadPoolConfig.getExecutor());
        // 等待所有任务完成
        CompletableFuture.allOf(albumListVoFuture, attributeValueCompletableFuture, userInfoCompletableFuture, categoryCompletableFuture).join();
        // 5. 上传到es
        elasticsearchTemplate.save(albumInfoIndex);
        // 6. 将专辑搜索匹配的字段存到搜索提示索引库
        saveSuugexIndex(albumInfoIndex.getAlbumTitle());
        saveSuugexIndex(albumInfoIndex.getAlbumIntro());
        saveSuugexIndex(albumInfoIndex.getAnnouncerName());
        // 7. 专辑上架更新布隆过滤器
        RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
        bloomFilter.add(albumId);
    }

    private void saveSuugexIndex(String value) {
        SuggestIndex suggestIndex = new SuggestIndex();
        suggestIndex.setId(IdUtil.getSnowflakeNextIdStr());
        suggestIndex.setTitle(value);
        suggestIndex.setKeyword(new Completion(new String[]{value}));
        suggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(value)}));
        suggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(value)}));
        elasticsearchTemplate.save(suggestIndex);
    }

    // 搜索栏查询 全文查询
    @SneakyThrows
    @Override
    public AlbumSearchResponseVo searchAlbums(AlbumIndexQuery query) {

        // 1. h获取参数 组装布尔查询的条件 匹配查询
        // 2. 过滤查询 根据三级分类id
        // 3. 排序
        // 4. 分页
        // 5. 获取结果,解析结果集

        // 执行查询
        // 获取查询条件构建器
//        NativeQueryBuilder builder = NativeQuery.builder();
//        // 构建查询
//        elasticsearchTemplate.search(builder.build(), AlbumInfoIndex.class);// 根据索引库查询
        SearchRequest.Builder builder = new SearchRequest.Builder();
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();
        String keyword = query.getKeyword();
        if (!StringUtils.isEmpty(keyword)) {
            boolQueryBuilder.must(bqm -> bqm.bool(bqmb -> bqmb
                    .should(bq1 -> bq1.match(bq1Term -> bq1Term.field("albumTitle").analyzer("ik_max_word").query(keyword)))
                    .should(bq2 -> bq2.match(bq2Term -> bq2Term.field("albumIntro").analyzer("ik_max_word").query(keyword)))
                    .should(bq3 -> bq3.term(bq3Term -> bq3Term.field("announcerName").value(keyword))
                            // .should(bq3 -> bq3.match(bq3Term -> bq3Term.field("announcerName").query(keyword).fuzziness("AUTO").operator(Operator.And).analyzer("ik_max_word")))
                    )));
        }
        // 分类id
        if (query.getCategory1Id() != null) {
            boolQueryBuilder.filter(fq -> fq.term(fqTerm -> fqTerm.field("category1Id").value(query.getCategory1Id())));
        }
        if (query.getCategory2Id() != null) {
            boolQueryBuilder.filter(fq -> fq.term(fqTerm -> fqTerm.field("category2Id").value(query.getCategory2Id())));
        }
        if (query.getCategory3Id() != null) {
            boolQueryBuilder.filter(fq -> fq.term(fqTerm -> fqTerm.field("category3Id").value(query.getCategory3Id())));
        }
        //
        List<String> attributeList = query.getAttributeList();
        if (attributeList != null && attributeList.size() > 0) {
            for (String attribute : attributeList) {
                String[] attributes = attribute.split(":");
                if (attributes.length == 2) {
                    String attributeId = attributes[0];
                    String attributeValueId = attributes[1];
                    boolQueryBuilder.filter(
                            f -> f.nested(n -> n.path("attributeValueIndexList")
                                    .query(nq -> nq.bool(qb -> qb
                                            .must(mq -> mq.term(tq -> tq.field("attributeValueIndexList.attributeId").value(attributeId)))
                                            .must(mq -> mq.term(tq -> tq.field("attributeValueIndexList.attributeValueId").value(attributeValueId))))))
                    );
                }
            }
        }
        // 排序 1综合排序(默认)  2播放量 3最近更新 降序排序
        String order = query.getOrder();
        if (!StringUtils.isEmpty(order)) {
            String[] orders = order.split(":");
            if (orders != null && orders.length == 2) {
                switch (orders[0]) {
                    case "2":
                        builder.sort(s -> s.field(fs -> fs.field("playStatNum").order(orders[1].equals("desc") ? SortOrder.Desc : SortOrder.Asc)));
                        break;
                    case "3":
                        builder.sort(s -> s.field(fs -> fs.field("createTime").order(orders[1].equals("desc") ? SortOrder.Desc : SortOrder.Asc)));
                        break;
                }
            }
        }
        builder.sort(s -> s.field(fs -> fs.field("hotScore").order(SortOrder.Desc)));
        // 分页
        builder.from((query.getPageNo() - 1) * query.getPageSize());
        // 设置高亮
        builder.highlight(Highlight.of(h -> h
                        .requireFieldMatch(false)  // 即使查询条件不直接匹配该字段也返回高亮
                        .fields("albumTitle", f -> f
                                .preTags("<font style='color:blue'>")
                                .postTags("</font>")
                        )
//                .fields("albumIntro", f -> f
//                        .preTags("<font style='color:green'>")
//                        .postTags("</font>")
//                )
                        // 如果需要支持announcerName的高亮，可以取消注释并添加以下配置
                        .fields("announcerName", f -> f
                                .preTags("<font style='color:red'>")
                                .postTags("</font>")
                                .requireFieldMatch(false)  // 特别重要：让keyword类型字段也能高亮
                        )
        ));
        builder.query(q -> q.bool(boolQueryBuilder.build()));// 将bool组合器放入查询条件构建器中builder 中
        SearchResponse<AlbumInfoIndex> response = elasticsearchClient.search(builder.build(), AlbumInfoIndex.class);
        // 获取查询结果打印在控制台
//        printResult(response)
        // 解析结果集
        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();
        albumSearchResponseVo.setPageNo(query.getPageNo());
        albumSearchResponseVo.setPageSize(query.getPageSize());
        albumSearchResponseVo.setTotal(response.hits().total().value());
        albumSearchResponseVo.setTotalPages((long) Math.ceil(albumSearchResponseVo.getTotal() / (double) albumSearchResponseVo.getPageSize()));

        List<AlbumInfoIndexVo> albumInfoIndexVoList = response.hits().hits().stream().map(
                hit -> {
                    AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
                    AlbumInfoIndex albumInfoIndex = hit.source();
                    BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);
                    // 获取查询到的高亮信息
                    if (hit.highlight() != null)
                        hit.highlight().forEach((key, value) -> {
                            switch (key) {
                                case "albumTitle":
                                    albumInfoIndexVo.setAlbumTitle(value.get(0));
                                    break;
                                case "albumIntro":
                                    albumInfoIndexVo.setAlbumIntro(value.get(0));
                                    break;
                                case "announcerName":
                                    albumInfoIndexVo.setAnnouncerName(value.get(0));
                                    break;
                            }
                        });
                    return albumInfoIndexVo;
                }
        ).toList();

        albumSearchResponseVo.setList(albumInfoIndexVoList);
        return albumSearchResponseVo;
    }

    @Override
    public void downAlbumInfoToEs(Long albumId) {
        // 下架商品
        // 1. 审核不通过
        // 2. 改为隐私
        // 3. 删除
        if (albumId == null) {
            return;
        }
        elasticsearchTemplate.delete(albumId.toString(), AlbumInfoIndex.class);
    }

    @SneakyThrows
    @Override
    public List<Map> getChannel(Long c1Id) {

        // 1. c1Id查询三级分类和三级分类列表
        Result<List<BaseCategory3>> category3ListResult = categoryFeignClient.findCategory3ListBycategory1Id(c1Id);
        if (category3ListResult == null
                || category3ListResult.getData() == null
                || !category3ListResult.getCode().equals(ResultCodeEnum.SUCCESS.getCode())) {
            throw new GuiguException(ResultCodeEnum.DATA_ERROR);
        }
        List<BaseCategory3> baseCategory3List = category3ListResult.getData();
        Map<Long, BaseCategory3> baseCategory3Map = baseCategory3List.stream().collect(Collectors.toMap(BaseCategory3::getId, baseCategory3 -> baseCategory3));
        // 2.  查询三级分类下的热门专辑列表
        SearchRequest.Builder builder = new SearchRequest.Builder();
        builder.index("albuminfo");
        // 设置查询条件
        builder.query(q -> q.terms(t -> t.field("category3Id").terms(term -> term.value(baseCategory3List.stream().map(

                BaseCategory3 -> {
                    FieldValue fieldValue = FieldValue.of(BaseCategory3.getId());
                    return fieldValue;
                }

        ).toList()))));
        // 不查询数据 只是聚合数据
        builder.size(0);
        builder.aggregations("c3IdAgg", a -> a.terms(t -> t.field("category3Id"))
                .aggregations("topN", agg2 -> agg2.topHits(th -> th.size(6)
                        .sort(agg2s -> agg2s.field(FieldSort.of(fs -> fs.field("hotScore").order(SortOrder.Desc)))))));

        SearchResponse<AlbumInfoIndex> response = elasticsearchClient.search(builder.build(), AlbumInfoIndex.class);
        Aggregate c3IdAgg = response.aggregations().get("c3IdAgg");


        List<Map> list = new ArrayList<>();
        // es 使用基本类型封装的词条是基本类型
        // long -> Lterms  keyword->Sterms
        c3IdAgg.lterms().buckets().array().forEach(
                c3Bucket -> {
                    Map map = new HashMap<>();
                    map.put("baseCategory3", baseCategory3Map.get(c3Bucket.key()));
                    List<AlbumInfoIndex> albumInfoIndexList = new ArrayList<>();
                    c3Bucket.aggregations().get("topN").topHits().hits().hits().forEach(
                            h -> {
                                AlbumInfoIndex albumInfoIndex = h.source().to(AlbumInfoIndex.class);
                                albumInfoIndexList.add(albumInfoIndex);
                            }
                    );
                    // map.put(c3Bucket.key(), albumInfoIndexList);
                    map.put("baseCategory3", baseCategory3List);
                    map.put("list", albumInfoIndexList);
                    list.add(map);
                });
        return list;
    }

    @SneakyThrows
    @Override
    public List<String> completeSuggest(String keyword) {
        // 1. 初始化搜素推荐的索引结构 SuggestIndex
        // 可以用 es 初始化 也可以用spring data repository模版类 初始化 项目启动时初始化索引库
        // 2. 数据上架的可以可以将文档分词后存到推荐索引库中
        // 3. 前端首页搜索框输入关键字，根据关键字去推荐索引库中查询数据，然后展示给用户
        SearchRequest.Builder builder = new SearchRequest.Builder();
        builder.suggest(s -> s
                .suggesters("sKeyword", s1 -> s1.prefix(keyword).completion(s1c -> s1c.field("keyword")
                        .skipDuplicates(true).size(10).fuzzy(s1cf -> s1cf.fuzziness("auto"))))
                .suggesters("sKeywordPinyin", s2 -> s2.prefix(keyword).completion(s2c -> s2c.field("keywordPinyin")
                        .skipDuplicates(true).size(10).fuzzy(s2cf -> s2cf.fuzziness("auto"))))
                .suggesters("sKeywordSequence", s3 -> s3.prefix(keyword).completion(s3c -> s3c.field("keywordSequence")
                        .skipDuplicates(true).size(10).fuzzy(s3cf -> s3cf.fuzziness("auto"))))
        );

        SearchResponse<SuggestIndex> response = elasticsearchClient.search(builder.build(), SuggestIndex.class);
        List<String> list = new ArrayList<>();
        List<Suggestion<SuggestIndex>> sKeyword = response.suggest().get("sKeyword");
        List<Suggestion<SuggestIndex>> sKeywordPinyin = response.suggest().get("sKeywordPinyin");
        List<Suggestion<SuggestIndex>> sKeywordSequence = response.suggest().get("sKeywordSequence");

        sKeyword.forEach(
                sk -> {
                    sk.completion().options().forEach(
                            sko -> {
                                list.add(sko.text());
                            }
                    );
                }
        );
        sKeywordPinyin.forEach(
                sk -> {
                    sk.completion().options().forEach(
                            sko -> {
                                list.add(sko.text());
                            }
                    );
                }
        );
        sKeywordSequence.forEach(
                sk -> {
                    sk.completion().options().forEach(
                            sko -> {
                                list.add(sko.text());
                            }
                    );
                }
        );
        return list;
    }

    @GuiguCache(lockKeyPrefix = RedisConstant.ALBUM_CACHE_LOCK_KEY
            , cacheKeyPrefix = RedisConstant.ALBUM_CACHE_KEY
            , cacheExpireTime = RedisConstant.CACHE_TIMEOUT
            ,bloomFilterKey = RedisConstant.ALBUM_BLOOM_FILTER
            ,isNeedBloomFilter = true)
    @Override
    public Map getAlbumInfoById(Long albumId) {

        Map map = new HashMap();

        // 1. 查询专辑的基本信息 albumInfo
        CompletableFuture<AlbumInfo> albumInfoFuture = CompletableFuture.supplyAsync(() -> {
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfoById2(albumId);
            AssertUtil.resultAssert(albumInfoResult, ResultCodeEnum.DATA_ERROR);
            AlbumInfo albumInfo = albumInfoResult.getData();
            map.put("albumInfo", albumInfo);
            return albumInfo;
        }, ServiceThreadPoolConfig.getExecutor());
        // 2. 查询统计信息
        CompletableFuture albumStatFuture = CompletableFuture.runAsync(() -> {
            Result<AlbumStatVo> albumStatVoResult = albumInfoFeignClient.getAlbumStatVo(albumId);
            AssertUtil.resultAssert(albumStatVoResult, ResultCodeEnum.DATA_ERROR);
            AlbumStatVo albumStatVo = albumStatVoResult.getData();
            map.put("albumStatVo", albumStatVo);
        }, ServiceThreadPoolConfig.getExecutor());

        // 3. 查询分类信息 baseCategoryView
        CompletableFuture<Void> BaseCategoryViewFuture = albumInfoFuture.thenAcceptAsync(albumInfo -> {
            Long category3Id = albumInfo.getCategory3Id();
            Result<BaseCategoryView> baseCategoryViewResult = categoryFeignClient.getBaseCategoryViewByCategory3Id(category3Id);
            AssertUtil.resultAssert(baseCategoryViewResult, ResultCodeEnum.DATA_ERROR);
            BaseCategoryView baseCategoryView = baseCategoryViewResult.getData();
            map.put("baseCategoryView", baseCategoryView);
        }, ServiceThreadPoolConfig.getExecutor());
        // 4. 查询主播名称 announcer
        CompletableFuture<Void> UserInfoVoFuture = albumInfoFuture.thenAcceptAsync(albumInfo -> {
            Long userId = albumInfo.getUserId();
            Result<UserInfoVo> userInfoByIdResult = userInfoFeignClient.getUserInfoById(userId);
            AssertUtil.resultAssert(userInfoByIdResult, ResultCodeEnum.DATA_ERROR);
            UserInfoVo userInfoVo = userInfoByIdResult.getData();
            map.put("announcer", userInfoVo);
        }, ServiceThreadPoolConfig.getExecutor());
        CompletableFuture.allOf(albumInfoFuture, albumStatFuture, BaseCategoryViewFuture, UserInfoVoFuture).join();

        return map;
    }

    @Override
    public List<AlbumInfoIndexVo> findRankingList(Long c1Id, String sort) {
        // 去es 根据c1Id查询所有专辑列表
        // 根据 sort 排序
//        try {
//            SearchRequest.Builder builder = new SearchRequest.Builder();
//            builder.query(q -> q.term(t -> t.field("category1Id").value(c1Id)));
//            builder.sort(s -> s.field(f -> f.field(sort).order(SortOrder.Desc)));
//            builder.size(10);
//            SearchResponse<AlbumInfoIndex> response = elasticsearchClient.search(builder.build(), AlbumInfoIndex.class);
//
//            List<AlbumInfoIndex> albumInfoIndexList = response.hits().hits().stream().map(hit -> hit.source()).collect(Collectors.toList());
//            List<AlbumInfoIndexVo> albumInfoIndexVoList = albumInfoIndexList.stream().map(albumInfoIndex -> {
//                AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
//                BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);
//                return albumInfoIndexVo;
//            }).collect(Collectors.toList());
//            return albumInfoIndexVoList;
//        } catch (Exception e) {
//            throw new RuntimeException("查询失败");
//        }
        // 1. 从redis获取热门数据即可
        List<AlbumInfoIndexVo> albumInfoIndexVoList = (List<AlbumInfoIndexVo>) redisTemplate
                .boundHashOps(RedisConstant.RANKING_KEY_PREFIX + c1Id).get(sort);
        return albumInfoIndexVoList;
    }

    @Override
    public void updateLatelyAlbumRanking() {
        // 外层循环便利所有一级分类的id
        // 内层循环便利所有的排序条件
        // subscribeStatNum  commentStatNum buyStatNum  playStatNum hotScore

        String[] sorts = {"playStatNum", "subscribeStatNum", "commentStatNum", "hotScore", "buyStatNum"};
        // 1. 远程调用查询所有的一级分类id列表
        // to 远程调用失败
        Result<List<Long>> baseCategory1ListResult = categoryFeignClient.getBaseCategory1List();
        AssertUtil.resultAssert(baseCategory1ListResult, ResultCodeEnum.DATA_ERROR);
        List<Long> c1Ids = baseCategory1ListResult.getData();
        // 便利一级列表
        c1Ids.forEach(
                c1Id -> {
                    // 便利 排序条件数组
                    for (String sort : sorts) {
                        // 去es 根据c1Id查询所有专辑列表
                        // 根据 sort 排序
                        try {
                            SearchRequest.Builder builder = new SearchRequest.Builder();
                            builder.query(q -> q.term(t -> t.field("category1Id").value(c1Id)));
                            builder.sort(s -> s.field(f -> f.field(sort).order(SortOrder.Desc)));
                            builder.size(10);
                            SearchResponse<AlbumInfoIndex> response = elasticsearchClient.search(builder.build(), AlbumInfoIndex.class);

                            List<AlbumInfoIndex> albumInfoIndexList = response.hits().hits().stream().map(hit -> hit.source()).collect(Collectors.toList());
                            List<AlbumInfoIndexVo> albumInfoIndexVoList = albumInfoIndexList.stream().map(albumInfoIndex -> {
                                AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
                                BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);
                                return albumInfoIndexVo;
                            }).collect(Collectors.toList());

                            // 讲查询到的数据存到redis hash结构中
                            redisTemplate.boundHashOps(RedisConstant.RANKING_KEY_PREFIX + c1Id)
                                    .put(sort, albumInfoIndexVoList);
                        } catch (Exception e) {
                            throw new RuntimeException("热门数据预热---查询失败");
                        }
                    }
                }
        );

    }
}
